﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Text.RegularExpressions;

namespace WhittleList.Bll
{
    public partial class User : IValidate
    {

        public virtual string DisplayName
        {
            get
            {
                return string.Concat(this.FirstName, " ", this.LastName);
            }
        }

        public virtual DateTime Created
        {
            get
            {
                return UTCCreated.ToLocalTime();
            }
        }

        public virtual DateTime LastUpdated
        {
            get
            {
                return UTCLastUpdated.ToLocalTime();
            }
        }

        public virtual DateTime? LastLoggedIn
        {
            get
            {
                DateTime? tmp = null;
                if (UTCLastLoggedIn.HasValue)
                    tmp = UTCLastLoggedIn.Value.ToLocalTime();
                return tmp;
            }
        }

        partial void OnValidate(System.Data.Linq.ChangeAction action)
        {
            if (!IsValid)
                throw new Exceptions.ValidationException("Validation Prevented Saving", GetRuleViolations());
        }

        public virtual bool IsValid
        {
            get { return GetRuleViolations().Count() == 0; }
        }

        public IEnumerable<RuleViolation> GetRuleViolations()
        {
            if (string.IsNullOrEmpty(this.EmailAddress))
                yield return new RuleViolation("EmailAddress", "You must enter your email address");
            else if(!Regex.IsMatch(EmailAddress, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
                yield return new RuleViolation("EmailAddress", "You must enter a valid email address");

            if (string.IsNullOrEmpty(this.FirstName))
                yield return new RuleViolation("FirstName", "You must enter your First Name");

            if (string.IsNullOrEmpty(this.LastName))
                yield return new RuleViolation("LastName", "You must enter your Last Name");

            if (string.IsNullOrEmpty(this.Password))
                yield return new RuleViolation("Password", "You must enter a Password");
        }

        public virtual bool SetPassword(string newPassword, string originalPassword)
        {
            bool retVal = false;
            if (!string.IsNullOrEmpty(Password) && string.IsNullOrEmpty(originalPassword))
            {
                throw new ApplicationException("Original password must be supplied if password already set.");
            }

            if (!string.IsNullOrEmpty(newPassword) && 
                (string.IsNullOrEmpty(Password) || (AuthenticatePassword(originalPassword) && !AuthenticatePassword(newPassword))))
            {
                CreateSalt();
                Password = ComputeSaltedHash(newPassword);
                retVal = true;
            }
            return retVal;
        }

        public virtual bool AuthenticatePassword(string password)
        {
            string tmp = ComputeSaltedHash(password);
            return tmp == this.Password;
        }

        private string ComputeSaltedHash(string password)
        {
            // Create Byte array of password string
            ASCIIEncoding encoder = new ASCIIEncoding();
            Byte[] secretBytes = encoder.GetBytes(password);

            // Create a new salt
            Byte[] saltBytes = new Byte[4];
            saltBytes[0] = (byte)(Salt >> 24);
            saltBytes[1] = (byte)(Salt >> 16);
            saltBytes[2] = (byte)(Salt >> 8);
            saltBytes[3] = (byte)(Salt);

            // append the two arrays
            Byte[] toHash = new Byte[secretBytes.Length + saltBytes.Length];
            Array.Copy(secretBytes, 0, toHash, 0, secretBytes.Length);
            Array.Copy(saltBytes, 0, toHash, secretBytes.Length, saltBytes.Length);

            SHA1 sha1 = SHA1.Create();
            Byte[] computedHash = sha1.ComputeHash(toHash);

            return encoder.GetString(computedHash);
        }

        private void CreateSalt()
        {
            Byte[] _saltBytes = new Byte[4];
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            rng.GetBytes(_saltBytes);

            Salt = ((((int)_saltBytes[0]) << 24) + (((int)_saltBytes[1]) << 16) + (((int)_saltBytes[2]) << 8) + ((int)_saltBytes[3]));
        }
    }
}
